<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
    <title>ShellCheck: SC2308 – `expr length` has unspecified results. Prefer `${#var}`.</title>
    <link rel="stylesheet" href="css/bootstrap.min.css" />
  </head>
  <body style="margin-left: auto; margin-right: auto; max-width: 800px">
    <h1>SC2308 – ShellCheck Wiki</h1>
    <a href="https://github.com/koalaman/shellcheck/wiki/SC2308">See this page on GitHub</a>
    <p style="display: none"><a href="index.html">Sitemap</a></p>
    <hr />
    <h2
id="expr-length-has-unspecified-results-prefer-var"><code>expr length</code>
has unspecified results. Prefer <code>${#var}</code>.</h2>
<p>or
<code>'expr match' has unspecified results. Prefer 'expr str : regex'.</code><br />
or
<code>'expr substr' has unspecified results. Prefer 'cut' or ${var#???}.</code><br />
or
<code>'expr index' has unspecified results. Prefer x=${var%%[chars]*}; $((${#x}+1)).</code></p>
<h3 id="problematic-code">Problematic code:</h3>
<div class="sourceCode" id="cb1"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb1-1"><a href="SC2308.html#cb1-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Find length of string</span></span>
<span id="cb1-2"><a href="SC2308.html#cb1-2" aria-hidden="true" tabindex="-1"></a><span class="va">length</span><span class="op">=</span><span class="va">$(</span><span class="fu">expr</span> length <span class="st">&quot;</span><span class="va">$var</span><span class="st">&quot;</span><span class="va">)</span></span>
<span id="cb1-3"><a href="SC2308.html#cb1-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-4"><a href="SC2308.html#cb1-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Match string against regex</span></span>
<span id="cb1-5"><a href="SC2308.html#cb1-5" aria-hidden="true" tabindex="-1"></a><span class="fu">expr</span> match <span class="st">&quot;</span><span class="va">$input</span><span class="st">&quot;</span> <span class="st">&quot;[0-9]*&quot;</span></span>
<span id="cb1-6"><a href="SC2308.html#cb1-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-7"><a href="SC2308.html#cb1-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Find character index in string</span></span>
<span id="cb1-8"><a href="SC2308.html#cb1-8" aria-hidden="true" tabindex="-1"></a><span class="va">pos</span><span class="op">=</span><span class="va">$(</span><span class="fu">expr</span> index <span class="st">&quot;</span><span class="va">$input</span><span class="st">&quot;</span> <span class="st">&quot;:&quot;</span><span class="va">)</span></span>
<span id="cb1-9"><a href="SC2308.html#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="SC2308.html#cb1-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Get substring by index</span></span>
<span id="cb1-11"><a href="SC2308.html#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="va">col2</span><span class="op">=</span><span class="va">$(</span><span class="fu">expr</span> substr <span class="st">&quot;foo    bar  baz&quot;</span> 8 5<span class="va">)</span></span></code></pre></div>
<h3 id="correct-code">Correct code:</h3>
<div class="sourceCode" id="cb2"><pre class="sourceCode sh"><code class="sourceCode bash"><span id="cb2-1"><a href="SC2308.html#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="co"># Find length of string</span></span>
<span id="cb2-2"><a href="SC2308.html#cb2-2" aria-hidden="true" tabindex="-1"></a><span class="va">length</span><span class="op">=</span><span class="va">${</span><span class="op">#</span><span class="va">var}</span></span>
<span id="cb2-3"><a href="SC2308.html#cb2-3" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-4"><a href="SC2308.html#cb2-4" aria-hidden="true" tabindex="-1"></a><span class="co"># Match string against regex</span></span>
<span id="cb2-5"><a href="SC2308.html#cb2-5" aria-hidden="true" tabindex="-1"></a><span class="fu">expr</span> <span class="st">&quot;</span><span class="va">$input</span><span class="st">&quot;</span> : <span class="st">&quot;[0-9]*&quot;</span></span>
<span id="cb2-6"><a href="SC2308.html#cb2-6" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-7"><a href="SC2308.html#cb2-7" aria-hidden="true" tabindex="-1"></a><span class="co"># Find character index in string</span></span>
<span id="cb2-8"><a href="SC2308.html#cb2-8" aria-hidden="true" tabindex="-1"></a><span class="va">pos</span><span class="op">=</span><span class="va">${input</span><span class="op">%%</span>:<span class="pp">*</span><span class="va">}</span> <span class="va">pos</span><span class="op">=</span><span class="va">$((${</span><span class="op">#</span><span class="va">pos}</span><span class="op">+</span><span class="dv">1</span><span class="va">))</span></span>
<span id="cb2-9"><a href="SC2308.html#cb2-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-10"><a href="SC2308.html#cb2-10" aria-hidden="true" tabindex="-1"></a><span class="co"># Get substring by index (bash)</span></span>
<span id="cb2-11"><a href="SC2308.html#cb2-11" aria-hidden="true" tabindex="-1"></a><span class="va">str</span><span class="op">=</span><span class="st">&quot;foo    bar  baz&quot;</span></span>
<span id="cb2-12"><a href="SC2308.html#cb2-12" aria-hidden="true" tabindex="-1"></a><span class="va">col2</span><span class="op">=</span><span class="st">&quot;</span><span class="va">${str</span><span class="op">:</span><span class="dv">7</span><span class="op">:</span><span class="dv">5</span><span class="va">}</span><span class="st">&quot;</span></span>
<span id="cb2-13"><a href="SC2308.html#cb2-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb2-14"><a href="SC2308.html#cb2-14" aria-hidden="true" tabindex="-1"></a><span class="co"># Get substring by index (POSIX)</span></span>
<span id="cb2-15"><a href="SC2308.html#cb2-15" aria-hidden="true" tabindex="-1"></a><span class="va">col2</span><span class="op">=</span><span class="st">&quot;</span><span class="va">$(</span><span class="bu">printf</span> <span class="st">&#39;foo    bar  baz\n&#39;</span> <span class="kw">|</span> <span class="fu">cut</span> <span class="at">-c</span> 8-12<span class="va">)</span><span class="st">&quot;</span></span></code></pre></div>
<h3 id="rationale">Rationale:</h3>
<p>You are using a <code>expr</code> with <code>length</code>,
<code>match</code>, <code>index</code>, or <code>substr</code>. These
forms did not make it into POSIX, and fail on platforms like MacOS and
FreeBSD. Consider replacing them with portable equivalents:</p>
<h4 id="length"><code>length</code></h4>
<p>can be trivially replaced with <code>${#var}</code></p>
<h4 id="match"><code>match</code></h4>
<p>can be trivially replaced with the POSIX form
<code>expr str : regex</code></p>
<h4 id="index"><code>index</code></h4>
<p>if you only need a numerical index as part of trying to extract a
piece of the string, consider replacing it with parameter expansion:</p>
<pre><code>str=&quot;mykey=myvalue&quot;
key=&quot;${str%%=*}&quot;    # Remove everything after first =, no index required
value=&quot;${str#*=}&quot;   # Remove everything before first =, no index required</code></pre>
<p>otherwise, you can find the index of the first <code>=</code> using
parameter expansion and string length:</p>
<pre><code>str=&quot;mykey=myvalue&quot;
x=${str%%=*}     # Assign x=&quot;mystr&quot;
index=$((${#x}+1))   # Add 1 to length of x</code></pre>
<h4 id="substr"><code>substr</code></h4>
<p>Extract a substring via character index is generally fragile. For
example, in this example, any minor changes to the format, including
just the version increasing from 8.9 to 8.10, will cause the following
snippet to fail:</p>
<pre><code>str=&quot;VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Feb 15 2021 12:29:39)&quot;
version=$(expr substr &quot;$str&quot; 19 3)</code></pre>
<p>Instead, consider a different approach:</p>
<pre><code>x=&quot;${str%% (*}&quot;     # Delete ` (` and everything after, giving &quot;VIM - Vi IMproved 8.2&quot;
version=&quot;${x##* }&quot;  # Delete everything before last space, giving &quot;8.2&quot;

# Get the fifth word separated by spaces
IFS=&quot; &quot; read -r _ _ _ _ version _ &lt;&lt; EOF
$str
EOF</code></pre>
<p>If you still want to use character index, this is trivially done in
Bash/Ksh with <code>${var:offset:length}</code> (0-based).</p>
<p>In POSIX, you can generally use <code>cut</code>, though be careful
if the value can contain multiple lines.</p>
<h3 id="exceptions">Exceptions:</h3>
<p>If you know your script will only run on platforms where these forms
are supported, like GNU or BusyBox, you can <a href="ignore.html">ignore</a>
this warning.</p>
<h3 id="related-resources">Related resources:</h3>
<ul>
<li>Help by adding links to BashFAQ, StackOverflow, man pages, POSIX,
etc!</li>
</ul>
    <hr />
    <p style='font-size: 80%'><a href="../index.html">ShellCheck</a> is a static analysis tool for shell scripts. This page is part of its documentation.</p>
  </body>
</html>


